/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/** **************************************************************************************
 * \file     sdrv_lld_dpe.c
 * \brief    DPE LLD Driver
 *
 * <table>
 * <tr><th>Date           <th>Version
 * <tr><td>2024/10/29     <td>1.0.0
 * </table>
 *****************************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif

#include "Dpe_IpPriv.h"
#include "Dpe_Ip.h"
#if (defined(CFG_PLATFORM_MCAL))
#include "RegHelper.h"
#include "Mcal_Cache.h"
#else /* CFG_PLATFORM_MCAL */
#include "reg.h"
#include "cache.h"
#endif /* CFG_PLATFORM_MCAL */

#if (defined(CFG_PLATFORM_MCAL))
#ifndef DPE_SYNC_WRITE_BUFFER_RANGE
#define DPE_SYNC_WRITE_BUFFER_RANGE(start, len) Mcal_CleanCache((uint32)(start), (len))
#endif

#ifndef DPE_SYNC_READ_BUFFER_RANGE
#define DPE_SYNC_READ_BUFFER_RANGE(start, len) Mcal_InvalidateCache((uint32)(start), (len))
#endif

#else  /* CFG_PLATFORM_MCAL */

#ifndef DPE_SYNC_WRITE_BUFFER_RANGE
#define DPE_SYNC_WRITE_BUFFER_RANGE(start, len) arch_clean_cache_range((addr_t)(start), (len))
#endif

#ifndef DPE_SYNC_READ_BUFFER_RANGE
#define DPE_SYNC_READ_BUFFER_RANGE(start, len) arch_invalidate_cache_range((addr_t)(start), (len))
#endif

#endif  /* CFG_PLATFORM_MCAL */


/***************************************************************************************************
 *                                  Private Variable Definitions                                   *
 **************************************************************************************************/
#ifdef CFG_PLATFORM_MCAL
#define DPE_START_SEC_CONST_UNSPECIFIED
#include "Dpe_MemMap.h"
#endif /* #ifdef CFG_PLATFORM_MCAL */

static const uint8_t CanFD_PayloadCode[65] = {
    0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U,
    /* 9 to 12 payload have DLC Code 12 Bytes */
    9, 9, 9, 9,
    /* 13 to 16 payload have DLC Code 16 Bytes */
    10, 10, 10, 10,
    /* 17 to 20 payload have DLC Code 20 Bytes */
    11, 11, 11, 11,
    /* 21 to 24 payload have DLC Code 24 Bytes */
    12, 12, 12, 12,
    /* 25 to 32 payload have DLC Code 32 Bytes */
    13, 13, 13, 13, 13, 13, 13, 13,
    /* 33 to 48 payload have DLC Code 48 Bytes */
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    /* 49 to 64 payload have DLC Code 64 Bytes */
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15
};

#ifdef CFG_PLATFORM_MCAL
#define DPE_STOP_SEC_CONST_UNSPECIFIED
#include "Dpe_MemMap.h"
#endif /* #ifdef CFG_PLATFORM_MCAL */

/***************************************************************************************************
 *                                  Private Function Declarations                                  *
 **************************************************************************************************/
#ifdef CFG_PLATFORM_MCAL
#define DPE_START_SEC_CODE
#include "Dpe_MemMap.h"
#endif /* #ifdef CFG_PLATFORM_MCAL */

static inline bool DpeVcan_Lld_TxFifoFull(uint32_t dpeBase, uint8_t vcanId)
{
    return (readl(dpeBase + DPE_VCAN_TX_FIFO_STATUS_OFFSET(vcanId)) &
                    DPE_VCAN_TX_FIFO_STATUS_FIFO_FULL_MASK) ? true : false;
}

/* Push packet with source address[31:3]. */
static inline void DpeVcan_Lld_Send(uint32_t dpeBase, uint8_t vcanId, uint32_t packetAddr)
{
    writel(DPE_VCAN_TX_PACKET_ADDR_SRC_ADDR(packetAddr >> 3U) | DPE_VCAN_TX_PACKET_ADDR_PACKET_SEND_MASK,
            dpeBase + DPE_VCAN_TX_PACKET_ADDR_OFFSET(vcanId));
}

/*
 * With tx drop enabled, VCAN packet pushed into tx fifo would be dropped when packet cannot be sent,
 * and there will be no send status generated.
 */
static inline void DpeVcan_Lld_EnableTxDrop(uint32_t dpeBase, uint8_t vcanId, bool en)
{
    uint32_t offset, value;

    offset = DPE_VCAN_CTRL_OFFSET(vcanId);
    value = readl(dpeBase + offset);
    if (true == en)
    {
        value |= DPE_VCAN_CTRL_RETRY_DROP_EN_MASK;
    }
    else
    {
        value &= ~DPE_VCAN_CTRL_RETRY_DROP_EN_MASK;
    }

    writel(value, dpeBase + offset);
}

/* Prepare buffer with source address[31:0]. */
static inline void DpeVcan_Lld_SetRxFifoStartAddr(uint32_t dpeBase, uint8_t vcanId, uint32_t addr)
{
    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        writel(addr, dpeBase + DPE_VCAN_RX_FIFO_CONFIG_OFFSET(vcanId));
    }
    else
    {
        writel(addr >> 3U, dpeBase + DPE_DIAGVCAN_FIFO_CONFIG_OFFSET);
    }
}

static inline uint32_t DpeVcan_Lld_GetRxFifoStartAddr(uint32_t dpeBase, uint8_t vcanId)
{
    uint32_t startAddr;

    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        startAddr = readl(dpeBase + DPE_VCAN_RX_FIFO_CONFIG_OFFSET(vcanId));
    }
    else
    {
        startAddr = readl(dpeBase + DPE_DIAGVCAN_FIFO_CONFIG_OFFSET) << 3U;
    }

    return startAddr;
}

/* Return true if rx fifo is empty. */
static inline bool DpeVcan_Lld_RxFifoEmpty(uint32_t dpeBase, uint8_t vcanId)
{
    bool ret = true;

    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        ret = (readl(dpeBase + DPE_VCAN_RX_FIFO_STATUS_OFFSET(vcanId)) &
                    DPE_VCAN_RX_FIFO_STATUS_FIFO_EMPTY_MASK) >>
                    DPE_VCAN_RX_FIFO_STATUS_FIFO_EMPTY_SHIFT;
    }
    else
    {
        ret = ((readl(dpeBase + DPE_DIAG_VCAN_RX_FIFO_STATUS_OFFSET) &
                    DPE_DIAG_VCAN_RX_FIFO_STATUS_FIFO_EMPTY_MASK) >>
                    DPE_DIAG_VCAN_RX_FIFO_STATUS_FIFO_EMPTY_SHIFT) ? true : false;
    }

    return ret;
}

/* Return the first valid packet index to be read in rxfifo. */
static inline uint8_t DpeVcan_Lld_GetRdPointer(uint32_t dpeBase, uint8_t vcanId)
{
    uint8_t rdPointer;

    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        rdPointer = (readl(dpeBase + DPE_VCAN_RX_PACKET_ADDR_OFFSET(vcanId)) &
            DPE_VCAN_RX_PACKET_ADDR_VCAN_FRIST_VALID_PACKET_INDEX_MASK) >>
            DPE_VCAN_RX_PACKET_ADDR_VCAN_FRIST_VALID_PACKET_INDEX_SHIFT;
    }
    else
    {
        rdPointer = (readl(dpeBase + DPE_DIAG_VCAN_RX_PACKET_ADDR_OFFSET) &
                            DPE_DIAG_VCAN_RX_PACKET_ADDR_VCAN_RX_1ST_PACKET_ADDR_MASK) >>
                            DPE_DIAG_VCAN_RX_PACKET_ADDR_VCAN_RX_1ST_PACKET_ADDR_SHIFT;
    }

    return rdPointer;
}

/* Free packet buffer by popping rdPointer of packet what has been read. */
static inline void DpeVcan_Lld_FreeRdPointer(uint32_t dpeBase, uint8_t vcanId,
                                                uint8_t rdPointer)
{
    uint32_t offset, value;

    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        offset = DPE_VCAN_RX_PACKET_ADDR_OFFSET(vcanId);
        value = readl(dpeBase + offset);
        value &= ~DPE_VCAN_RX_PACKET_ADDR_VCAN_RX_RD_PTR_INDEX_MASK;
        value |= (DPE_VCAN_RX_PACKET_ADDR_VCAN_RX_RD_PTR_INDEX(rdPointer) |
                    DPE_VCAN_RX_PACKET_ADDR_PACKET_POP_MASK);
        writel(value, dpeBase + offset);
    }
    else
    {
        value = readl(dpeBase + DPE_DIAG_VCAN_RX_PACKET_ADDR_OFFSET);
        value &= ~DPE_DIAG_VCAN_RX_PACKET_ADDR_DVCAN_RX_RD_PTR_INDEX_MASK;
        value |= (DPE_DIAG_VCAN_RX_PACKET_ADDR_DVCAN_RX_RD_PTR_INDEX(rdPointer) |
                    DPE_DIAG_VCAN_RX_PACKET_ADDR_PACKET_POP_MASK);
        writel(value, dpeBase + DPE_DIAG_VCAN_RX_PACKET_ADDR_OFFSET);
    }
}

/* Information for all valid status slot: 1 for valid, 0 for invalid. */
static inline uint32_t DpeVcan_Lld_GetValidStatSlots(uint32_t dpeBase, uint8_t vcanId)
{
    return readl(dpeBase + DPE_VCANCTRL_OFFSET(vcanId)) & DPE_VCANCTRL_STATUS_MASK;
}

/* Indicate status for done, timeout and drop. */
static inline uint32_t DpeVcan_Lld_GetStatSlotVal(uint32_t dpeBase, uint8_t vcanId,
                                                    uint32_t slotIdx)
{
    return readl(dpeBase + DPE_VCAN_TX_SEND_STATUS_OFFSET(vcanId, slotIdx));
}

static inline void DpeVcan_Lld_FreeStatSlot(uint32_t dpeBase, uint8_t vcanId,
                                                uint32_t slotIdx)
{
    uint32_t offset, value;

    offset = DPE_VCAN_TX_SEND_STATUS_OFFSET(vcanId, slotIdx);
    value = readl(dpeBase + offset);
    value &= ~DPE_VCAN_TX_SEND_STATUS_PACKET_STATUS_VLD_MASK;
    writel(value, dpeBase + offset);
}

static inline void Dpe_Lld_MaskCanReq(uint32_t dpeBase, uint32_t canId, bool enable)
{
    uint32_t value = enable ? 1 : 0;
    writel(value, dpeBase + DPE_CAN_REQ_MASK_OFFSET(canId));
}

/* Use address[31:3]. */
static inline void Dpe_Lld_ConfigCanBufferBaseAddr(uint32_t dpeBase, uint8_t canId, uint32_t addr)
{
    writel(DPE_CAN_BUFFER_BASE_ADDR_BASE_ADDR((addr >> 3U)),
            dpeBase + DPE_CAN_BUFFER_BASE_ADDR_OFFSET(canId));
}
/* Use address[31:16]. */
static inline void Dpe_Lld_ConfigCanFDBaseAddr(uint32_t dpeBase, uint8_t canId, uint32_t addr)
{
    writel(DPE_CANFD_BASE_ADDR_BASE_ADDR((addr >> 16U)),
            dpeBase + DPE_CANFD_BASE_ADDR_OFFSET(canId));
}

/* Enabling means preventing txqueue from buffering any newly arriving packets
 * and invalidating all old packets in the txqueue.
 * Disabling means allowing txqueue to buffer new packets.
 */
static inline void DpeQueue_Lld_QueueFlush(uint32_t dpeBase, uint32_t txqueueIdx, bool en)
{
    uint32_t offset, value;

    offset = DPE_CAN_TXQUEUE_CONFIG_OFFSET(txqueueIdx);
    value = readl(dpeBase + offset);
    if (true == en)
    {
        value |= DPE_CAN_TXQUEUE_CONFIG_QUEUE_FLUSH_MASK;
    }
    else
    {
        value &= ~DPE_CAN_TXQUEUE_CONFIG_QUEUE_FLUSH_MASK;
    }

    writel(value, dpeBase + offset);
}

/* Note: Check when txqueue is flushed. */
static inline bool DpeQueue_Lld_QueueFlushDone(uint32_t dpeBase, uint32_t txqueueIdx)
{
    return (readl(dpeBase + DPE_CAN_TXQUEUE_CONFIG_OFFSET(txqueueIdx)) &
            DPE_CAN_TXQUEUE_CONFIG_QUEUE_FLUSH_DONE_MASK) ? true : false;
}

/* Enabling means allowing txqueue to work without message buffer status check.
 * Disabling means requesting txqueue to work with message buffer status check.
 *
 * Note: Disabling when txqueue is working normally will cause unpredictable behavior.
 */
static inline void DpeQueue_Lld_QueueSendingFlush(uint32_t dpeBase, uint32_t txqueueIdx, bool en)
{
    uint32_t offset, value;

    offset = DPE_CAN_TXQUEUE_CONFIG_OFFSET(txqueueIdx);
    value = readl(dpeBase + offset);
    if (true == en)
    {
        value |= DPE_CAN_TXQUEUE_CONFIG_QUEUE_SENDING_FLUSH_MASK;
    }
    else
    {
        value &= ~DPE_CAN_TXQUEUE_CONFIG_QUEUE_SENDING_FLUSH_MASK;
    }
    writel(value, dpeBase + offset);
}

static uint8_t Dpe_Lld_ComputePayloadLen(uint8_t dlcVal)
{
    uint8_t retPayloadLen = 0U;

    if (dlcVal <= 8U)
    {
        retPayloadLen = dlcVal;
    }
    else if (dlcVal == 9U)
    {
        retPayloadLen = 12U;
    }
    else if (dlcVal == 10U)
    {
        retPayloadLen = 16U;
    }
    else if (dlcVal == 11U)
    {
        retPayloadLen = 20U;
    }
    else if (dlcVal == 12U)
    {
        retPayloadLen = 24U;
    }
    else if (dlcVal == 13U)
    {
        retPayloadLen = 32U;
    }
    else if (dlcVal == 14U)
    {
        retPayloadLen = 48U;
    }
    else if (dlcVal == 15U)
    {
        retPayloadLen = 64U;
    }
    else
    {
        /* Do nothing. */
    }

    return retPayloadLen;
}

static uint8_t Dpe_Lld_ComputeDlcVal(uint8_t payloadSize)
{
    uint8_t retDlcVal = 0xFFU;

    if (payloadSize <= 64U)
    {
        retDlcVal = CanFD_PayloadCode[payloadSize];
    }
    else
    {
        /* The argument is not a valid payload size,
         * so return invalid value 0xFF.
         */
    }

    return retDlcVal;
}

static bool Dpe_Lld_DpeIdle(uint32_t dpeBase)
{
    return (((readl(dpeBase + DPE_CONFIG_OFFSET) & DPE_CONFIG_STATUS_MASK) >>
                DPE_CONFIG_STATUS_SHIFT) == DPE_IDLE_STATUS) ? true : false;
}

/* True means allow vcan send timeout status generated in scene
 * that vcan send cannot be finished, because after busoff or de-init for can controller,
 * SW will clean memory of message buffer.
 */
static inline void DpeQueue_Lld_AllowTimeout(uint32_t dpeBase, uint8_t txqueueIdx, bool en)
{
    uint32_t value;
    value = readl(dpeBase + DPE_CAN_PACKET_SEND_STATUS_OFFSET(txqueueIdx));
    if (true == en)
    {
        value &= ~DPE_CAN_PACKET_SEND_STATUS_CAN_MB_SEND_TIMEOUT_CLR_MASK;
    }
    else
    {
        value |= DPE_CAN_PACKET_SEND_STATUS_CAN_MB_SEND_TIMEOUT_CLR_MASK;
    }

    writel(value, dpeBase + DPE_CAN_PACKET_SEND_STATUS_OFFSET(txqueueIdx));
}

/***************************************************************************************************
 *                                  Global Function Declarations                                   *
 **************************************************************************************************/

/**
 * @brief: Implement a memcpy function independent of platform.
 *
 * This function is used to copy data from source address to destination address byte by byte.
 *
 * @param[in] dst: Destination address.
 * @param[in] src: Source address.
 * @param[in] length: Length of data to be copied.
 *
 * @note: It's a weak function, and it's recommended to implement it for better performance.
 */
__attribute__((weak)) void Dpe_MemCpy(uint8_t *dst, uint8_t *src, uint32_t length)
{
    uint32_t idx;

    for (idx = 0U; idx < length; idx++) {
        dst[idx] = src[idx];
    }
}

void DpeVcan_Lld_Config(uint32_t dpeBase, uint8_t vcanId, uint32_t rxFifoAddr, bool enableTxDrop)
{
    DpeVcan_Lld_SetRxFifoStartAddr(dpeBase, vcanId, rxFifoAddr);

    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        DpeVcan_Lld_EnableTxDrop(dpeBase, vcanId, enableTxDrop);
    }
}

void DpeVcan_Lld_EnableInt(uint32_t dpeBase, uint8_t vcanId, uint8_t intEnable)
{
    uint32_t offset, value;

    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        offset = DPE_VCANINTEN_OFFSET(vcanId);
    }
    else
    {
        offset = DPE_DVCANINTEN_OFFSET;
    }

    value = readl(dpeBase + offset);
    value |= intEnable;
    writel(value, dpeBase + offset);
}

void DpeVcan_Lld_DisableInt(uint32_t dpeBase, uint8_t vcanId, uint8_t intEnable)
{
    uint32_t offset, value;

    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        offset = DPE_VCANINTEN_OFFSET(vcanId);
    }
    else
    {
        offset = DPE_DVCANINTEN_OFFSET;
    }

    value = readl(dpeBase + offset);
    value &= ~intEnable;
    writel(value, dpeBase + offset);
}

uint32_t DpeVcan_Lld_GetIntStatus(uint32_t dpeBase, uint8_t vcanId)
{
    uint32_t offset, retVal;
    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        offset = DPE_VCANINTSTAT_OFFSET(vcanId);
        retVal = readl(dpeBase + offset) & DPE_VCANINTSTAT_MASK;
    }
    else
    {
        retVal = readl(dpeBase + DPE_DVCANINTSTAT_OFFSET) &
                            (DPE_DVCANINTSTAT_DVCAN_REC_MASK |
                            DPE_DVCANINTSTAT_DVCAN_REC_DROP_MASK);
    }

    return retVal;
}

uint32_t DpeVcan_Lld_GetIntEnable(uint32_t dpeBase, uint8_t vcanId)
{
    uint32_t offset, retVal;
    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        offset = DPE_VCANINTEN_OFFSET(vcanId);
        retVal = readl(dpeBase + offset) & DPE_VCANINTEN_MASK;
    }
    else
    {
        retVal = readl(dpeBase + DPE_DVCANINTEN_OFFSET) &
                        (DPE_DVCANINTEN_DVCAN_REC_MASK |
                        DPE_DVCANINTEN_DVCAN_REC_DROP_MASK);
    }

    return retVal;
}

void DpeVcan_Lld_ClearIntStatus(uint32_t dpeBase, uint8_t vcanId,
                                    uint32_t intStatus)
{
    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        writel(intStatus, dpeBase + DPE_VCANINTCLR_OFFSET(vcanId));
    }
    else
    {
        writel(intStatus, dpeBase + DPE_DVCANINTCLR_OFFSET);
    }
}

void DpeVcan_Lld_HandleSendStatus(uint32_t dpeBase, uint8_t vcanId,
                                        DpeVcan_CallbackFuncType callback, void *arg)
{
    uint32_t statSlotVldMsk;
    uint32_t pktId = 0xFFFFFFFFU;
    Dpe_VcanDropReasonType dropReason;
    Dpe_VcanStatusType status = DPE_VCAN_SEND_STATUS_INVALID;
    uint32_t slotVal;
    uint32_t slotIdx;
    bool needFree = false;

    statSlotVldMsk = DpeVcan_Lld_GetValidStatSlots(dpeBase, vcanId);

    for (slotIdx = 0U; slotIdx < DPE_VCAN_SEND_STAT_SLOT_NUM; slotIdx++) {
        if (!(statSlotVldMsk & (1U << slotIdx)))
        {
            continue;
        }

        /* 1. Get valid status slot value about status. */
        slotVal = DpeVcan_Lld_GetStatSlotVal(dpeBase, vcanId, slotIdx);

        if (!(slotVal & DPE_VCAN_TX_SEND_STATUS_PACKET_STATUS_VLD_MASK))
        {
            /* 2. Check whether status slot is valid again. */
            status = DPE_VCAN_SEND_STATUS_INVALID;
            needFree = false;
        }
        else if (slotVal & DPE_VCAN_TX_SEND_STATUS_SEND_DONE_MASK)
        {
            /* 3. Check send done status and packet id. */
            status = DPE_VCAN_SEND_DONE;
            /* check whether status is out of date if reset happens
             * for core where vcan is deployed.
             */
            pktId = slotVal & DPE_VCAN_TX_SEND_STATUS_PACKET_ID_MASK;
            needFree = true;
        }
        else if (slotVal & DPE_VCAN_TX_SEND_STATUS_SEND_TIMEOUT_MASK)
        {
            /* 4. Check send timeout status.
             * For sw, send status both with timeout and drop/done would not happen.
             * because sw will set timeout only when mb send can not be finished.
             */
            status = DPE_VCAN_SEND_TIMEOUT;
            pktId = slotVal & DPE_VCAN_TX_SEND_STATUS_PACKET_ID_MASK;
            needFree = true;
        }
        else if (slotVal & DPE_VCAN_TX_SEND_STATUS_PACKET_DROP_MASK)
        {
            /* 5. Parse send drop status and reason. */
            dropReason = (Dpe_VcanDropReasonType)((slotVal &
                            DPE_VCAN_TX_SEND_STATUS_DROP_REASON_MASK) >>
                            DPE_VCAN_TX_SEND_STATUS_DROP_REASON_SHIFT);
            pktId = slotVal & DPE_VCAN_TX_SEND_STATUS_PACKET_ID_MASK;
            needFree = true;
            if (DPE_VCAN_FLUSHED == dropReason)
            {
                /* Vcan packet is flushed by DPE. */
                status = DPE_VCAN_SEND_FLUSHED;
            }
            else if (DPE_VCAN_FILTERED == dropReason)
            {
                /* Vcan packet hit PFC, and action is drop. */
                status = DPE_VCAN_SEND_FILTERED;
            }
            else if (DPE_VCAN_REPLACED == dropReason)
            {
                /* Old packet will be replaced by newer one with same can id
                 * when txqueue replacement enabled.
                 */
                status = DPE_VCAN_SEND_REPLACED;
            }
            else
            {
                /* For unrecg and wrong dlc vcan packet. */
                status = DPE_VCAN_SEND_UNEXPECTED;
            }
        }
        else
        {
            /* 6. Do nothing.
             * For example, there are two valid slots, 1st one for sending and 2th for drop.
             * SW must not to free slot for 1st one because send hasn't been finished.
             */
            needFree = false;
        }

        if (true == needFree) {
            /* 7. Free valid status slot. */
            DpeVcan_Lld_FreeStatSlot(dpeBase, vcanId, slotIdx);
            /* Calling Callback Function if has one. */
            if (callback != NULL)
            {
                callback(arg, status, NULL, pktId);
            }
        }
    }
}

void DpeVcan_Lld_Start(uint32_t dpeBase, uint8_t vcanId, uint8_t intEnable)
{
    uint32_t value;

    DpeVcan_Lld_EnableInt(dpeBase, vcanId, intEnable);

    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        value = readl(dpeBase + DPE_VCAN_CTRL_OFFSET(vcanId));
        value |= DPE_VCAN_CTRL_EN_MASK;
        writel(value, dpeBase + DPE_VCAN_CTRL_OFFSET(vcanId));
    }
    else
    {
        value = readl(dpeBase + DPE_DIAGVCAN_FIFO_CONFIG_OFFSET);
        value |= DPE_DIAGVCAN_FIFO_CONFIG_EN_MASK;
        writel(value, dpeBase + DPE_DIAGVCAN_FIFO_CONFIG_OFFSET);
    }
}

void DpeVcan_Lld_Stop(uint32_t dpeBase, uint8_t vcanId)
{
    uint32_t value;

    if (vcanId != DPE_DIAG_VCAN_CH_ID)
    {
        value = readl(dpeBase + DPE_VCAN_CTRL_OFFSET(vcanId));
        value &= ~DPE_VCAN_CTRL_EN_MASK;
        writel(value, dpeBase + DPE_VCAN_CTRL_OFFSET(vcanId));
    }
    else
    {
        value = readl(dpeBase + DPE_DIAGVCAN_FIFO_CONFIG_OFFSET);
        value &= ~DPE_DIAGVCAN_FIFO_CONFIG_EN_MASK;
        writel(value, dpeBase + DPE_DIAGVCAN_FIFO_CONFIG_OFFSET);
    }
}

int DpeVcan_Lld_Write(uint32_t dpeBase, uint8_t vcanId, uint32_t *txPacketAddr,
                                    DpeVcan_FrameType *frame, uint32_t packetId)
{
    int ret = DPE_OK;
    uint32_t headerTemp = 0U, idTemp = 0U;
    uint8_t *packetData;
    uint8_t paddingVal = frame->paddingVal;
    uint8_t dlcVal = Dpe_Lld_ComputeDlcVal(frame->length);
    uint8_t payloadLen = Dpe_Lld_ComputePayloadLen(dlcVal);
    uint8_t bufferIdx;

    /* 1. Check VCAN_TX_FIFO_STATUS.FIFO_FULL. If FULL, then return BUSY. */
    if (DpeVcan_Lld_TxFifoFull(dpeBase, vcanId))
    {
        ret = DPE_BUSY;
    }
    else
    {
        /* 2. If there's room, use txPacketAddr addr to construct one packet:
        * {dlc, edl, ide}, id, payload, packet id.
        */
        headerTemp = PACKET_BUFFER_DLC(dlcVal) |
                        PACKET_BUFFER_IDE(frame->isExtend ? 1 : 0) |
                        PACKET_BUFFER_EDL(frame->isCanfd ? 1 : 0);
        PACKET_HEADER_FIELD(txPacketAddr) = headerTemp;

        if (true == frame->isExtend)
        {
            idTemp = PACKET_BUFFER_CAN_ID_EXT(frame->id);
        }
        else
        {
            idTemp = PACKET_BUFFER_CAN_ID_STD(frame->id);
        }
        PACKET_ID_FIELD(txPacketAddr) = idTemp;

        packetData = (uint8_t *)PACKET_DATA_FIELD(txPacketAddr);
        Dpe_MemCpy(packetData, frame->dataBuffer, frame->length);
        for (bufferIdx = frame->length; bufferIdx < payloadLen; bufferIdx++)
        {
            packetData[bufferIdx] = paddingVal;
        }

        PACKET_TAIL_FIELD(txPacketAddr) = PACKET_BUFFER_PKT_ID(packetId);

        DPE_SYNC_WRITE_BUFFER_RANGE(txPacketAddr, DPE_VCAN_FIFO_ELEMENT_SIZE);

        /* 3. Send write request. */
        DpeVcan_Lld_Send(dpeBase, vcanId, (uint32_t)txPacketAddr);
    }

    return ret;
}

int DpeVcan_Lld_Read(uint32_t dpeBase, uint8_t vcanId, DpeVcan_FrameType *frame, uint8_t *srcBusId)
{
    uint32_t *packetAddr;
    uint32_t rxFifoAddr;
    uint8_t rdPointer;
    uint8_t freeRdPointer;
    uint32_t headerTemp = 0U, idTemp = 0U;
    int ret = DPE_OK;

    if (DpeVcan_Lld_RxFifoEmpty(dpeBase, vcanId))
    {
        ret =  DPE_NOK;
    }
    else
    {
        /* 1. Get rx packet addr. */
        rdPointer = DpeVcan_Lld_GetRdPointer(dpeBase, vcanId);
        rxFifoAddr = DpeVcan_Lld_GetRxFifoStartAddr(dpeBase, vcanId);
        packetAddr = (uint32_t *)(rxFifoAddr + rdPointer * DPE_VCAN_FIFO_ELEMENT_SIZE);
        DPE_SYNC_READ_BUFFER_RANGE(packetAddr, DPE_VCAN_FIFO_ELEMENT_SIZE);

        /* 2. Read packet from rx fifo: {dlc, edl, ide}, id, payload and src_bus. */
        headerTemp = PACKET_HEADER_FIELD(packetAddr);
        idTemp = PACKET_ID_FIELD(packetAddr);

        frame->isCanfd = (headerTemp & PACKET_BUFFER_EDL_MASK) ? true : false;
        frame->isExtend = (headerTemp & PACKET_BUFFER_IDE_MASK) ? true : false;
        if (true == frame->isExtend)
        {
            frame->id = idTemp & PACKET_BUFFER_CAN_ID_EXT_MASK;
        }
        else
        {
            frame->id = (idTemp & PACKET_BUFFER_CAN_ID_STD_MASK) >> PACKET_BUFFER_CAN_ID_STD_SHIFT;
        }
        frame->length = Dpe_Lld_ComputePayloadLen((headerTemp & PACKET_BUFFER_DLC_MASK) >>
                                                    PACKET_BUFFER_DLC_SHIFT);
        Dpe_MemCpy(frame->dataBuffer, (uint8_t *)(PACKET_DATA_FIELD(packetAddr)), frame->length);

        *srcBusId = (PACKET_TAIL_FIELD(packetAddr) & PACKET_BUFFER_SRC_BUS_MASK) >>
                                                        PACKET_BUFFER_SRC_BUS_SHIFT;

        /* 3. Pop read pointer.
         * For example, if rdPointer = 0x3, then pop value of 0x4.
         */
        freeRdPointer = (rdPointer + 1) % DPE_VCAN_RX_FIFO_ELEMENT_NUM;
        DpeVcan_Lld_FreeRdPointer(dpeBase, vcanId, freeRdPointer);
    }

    return ret;
}

void DpeVcan_Lld_RecvPolling(uint32_t dpeBase, uint8_t vcanId, uint8_t pollingIntStat,
                                DpeVcan_CallbackFuncType callback, void *arg)
{
    uint32_t intStatus, pollingIntFlag;
    uint8_t dataBuf[64];
    Dpe_VcanStatusType status;
    DpeVcan_FrameType frame;
    frame.dataBuffer = dataBuf;

    /* 1. Get interrupt status and clear interrupt. */
    intStatus = DpeVcan_Lld_GetIntStatus(dpeBase, vcanId);
    pollingIntFlag = intStatus & pollingIntStat;

    DpeVcan_Lld_ClearIntStatus(dpeBase, vcanId, pollingIntFlag);

    /* 2. Handle rec frame int status if has one. */
    if (pollingIntFlag & DPE_VCANINTSTAT_VCAN_REC_MASK)
    {
        bool readDone = false;
        int readStat;
        uint8_t srcBusId = 0xFFU;
        /* Read out all rec frames in rx fifo. */
        while (!readDone) {
            readStat = DpeVcan_Lld_Read(dpeBase, vcanId, &frame, &srcBusId);
            if (DPE_OK == readStat)
            {
                status = DPE_VCAN_REC;
                /* Calling Callback Function if has one. */
                if (callback != NULL)
                {
                    callback(arg, status, &frame, srcBusId);
                }
            }
            else
            {
                readDone = true;
            }
        }
    }

    /* 3. Handle rec drop int status if has one. */
    if (pollingIntFlag & DPE_VCANINTSTAT_VCAN_REC_DROP_MASK)
    {
        status = DPE_VCAN_REC_DROP;
        /* Calling Callback Function if has one. */
        if (callback != NULL)
        {
            callback(arg, status, NULL, 0);
        }
    }
}

void DpeVcan_Lld_SendStatusPolling(uint32_t dpeBase, uint8_t vcanId, uint8_t pollingIntStat,
                                    DpeVcan_CallbackFuncType callback, void *arg)
{
    uint32_t intStatus, pollingIntFlag;

    /* 1. Get interrupt status and clear interrupt. */
    intStatus = DpeVcan_Lld_GetIntStatus(dpeBase, vcanId);
    pollingIntFlag = intStatus & pollingIntStat;

    DpeVcan_Lld_ClearIntStatus(dpeBase, vcanId, pollingIntFlag);

    /* 2. Handle send int status if any of {done, timeout, drop} occurred. */
    if (pollingIntFlag)
    {
        DpeVcan_Lld_HandleSendStatus(dpeBase, vcanId, callback, arg);
    }
}

void DpeVcan_Lld_IrqHandler(uint32_t dpeBase, uint8_t vcanId,
                            DpeVcan_CallbackFuncType callback, void *arg)
{
    uint32_t intStatus, intEnable, intFlag;
    uint8_t dataBuf[64];
    Dpe_VcanStatusType status;
    DpeVcan_FrameType frame;
    frame.dataBuffer = dataBuf;

    /* 1. Get interrupt status&&enable and clear interrupt. */
    intStatus = DpeVcan_Lld_GetIntStatus(dpeBase, vcanId);
    intEnable = DpeVcan_Lld_GetIntEnable(dpeBase, vcanId);
    intFlag = intStatus & intEnable;

    DpeVcan_Lld_ClearIntStatus(dpeBase, vcanId, intFlag);

    /* 2. Handle rec frame interrupt if has one. */
    if (intFlag & DPE_VCANINTSTAT_VCAN_REC_MASK)
    {
        bool readDone = false;
        int readStat;
        uint8_t srcBusId = 0xFFU;
        /* Read out all rec frames in rx fifo. */
        while (!readDone) {
            readStat = DpeVcan_Lld_Read(dpeBase, vcanId, &frame, &srcBusId);
            if (DPE_OK == readStat)
            {
                status = DPE_VCAN_REC;
                /* Calling Callback Function if has one. */
                if (callback != NULL)
                {
                    callback(arg, status, &frame, srcBusId);
                }
            }
            else
            {
                readDone = true;
            }
        }
    }

    /* 3. Handle rec drop interrupt if has one. */
    if (intFlag & DPE_VCANINTSTAT_VCAN_REC_DROP_MASK)
    {
        status = DPE_VCAN_REC_DROP;
        /* Calling Callback Function if has one. */
        if (callback != NULL)
        {
            callback(arg, status, NULL, 0);
        }
    }

    /* 4. Handle send interrupts if any of {done, timeout, drop} occurred. */
    if (intFlag & (DPE_VCANINTSTAT_VCAN_SEND_DONE_MASK |
                    DPE_VCANINTSTAT_VCAN_SEND_TIMEOUT_MASK |
                    DPE_VCANINTSTAT_VCAN_SEND_DROP_MASK))
    {
        DpeVcan_Lld_HandleSendStatus(dpeBase, vcanId, callback, arg);
    }
}

void Dpe_Lld_RoutingTableConfig(uint32_t dpeBase, uint32_t *indexTblAddr, uint32_t indexSizeInByte,
                                                  uint32_t *ctxTblAddr, uint32_t ctxSizeInByte)
{
    uint32_t i, maxIndexSize;
    uint32_t *indexReg = (uint32_t *)(dpeBase + DPE_RTLB_INDEX_ADDR_START_OFFSET);
    uint32_t indexSizeInWord = indexSizeInByte / 4U;

    /*
     * 1. Copy index table to dpe index registers.
     * Load operation requires 4-byte alignment.
     */
    for (i = 0U; i < indexSizeInWord; i++) {
        indexReg[i] = indexTblAddr[i];
    }

    /*
     * 2. If the index of RTLB is sorted from small to large,
     * SW should fill unused index registers with 0xFFU.
     */
    maxIndexSize = DPE_MAX_RTLB_INDEX_ENTRY_NUM * 2U;
    for (i = indexSizeInWord; i < maxIndexSize; i++) {
        indexReg[i] = 0xFFFFFFFFU;
    }

    DPE_SYNC_WRITE_BUFFER_RANGE(ctxTblAddr, ctxSizeInByte);

    /* 3. Configure route table base address register for context of RTLB. */
    writel(DPE_ROUTE_TABLE_BASE_ADDR_BASE_ADDR(((uint32_t)ctxTblAddr) >> 3U),
            dpeBase + DPE_ROUTE_TABLE_BASE_ADDR_OFFSET);
}

void Dpe_Lld_Start(uint32_t dpeBase)
{
    uint32_t value;

    value = readl(dpeBase + DPE_CONFIG_OFFSET);
    value &= ~DPE_CONFIG_CAN_TX_TIMEOUT_CNT_MASK;
    value |= DPE_CONFIG_CAN_TX_TIMEOUT_CNT(DPE_TIME_COUNTER_UNIT_VAL);
#if !DPE_CONVERT_ENDIAN
    value |= DPE_CONFIG_VCAN_LITTLE_ENDIAN_EN_MASK;
#endif
    value |= DPE_CONFIG_DPE_EN_MASK;
    value &= ~DPE_CONFIG_FLUSH_ALL_MASK;
    writel(value, dpeBase + DPE_CONFIG_OFFSET);
}

int Dpe_Lld_Stop(uint32_t dpeBase)
{
    int ret = DPE_NOK;
    uint32_t value;
    uint32_t timeoutCnt = DPE_EN_TIMEOUT_COUNTER;

    value = readl(dpeBase + DPE_CONFIG_OFFSET);
    /*
     * ERRATA DIPDPE-10:
     * The VCAN send status does not return after dpe disabled,
     * so we use DPE_CONFIG_FLUSH_ALL_MASK instead of DPE_CONFIG_DPE_EN_MASK.
     */
    value |= DPE_CONFIG_FLUSH_ALL_MASK;
    writel(value, dpeBase + DPE_CONFIG_OFFSET);

    while (timeoutCnt--) {
        if (Dpe_Lld_DpeIdle(dpeBase))
        {
            ret = DPE_OK;
            break;
        }
    }

    return ret;
}

void Dpe_Lld_ConfigMirrorBus(uint32_t dpeBase, Dpe_BusIdType bus, uint32_t subQid)
{
    uint32_t value;

    value = DPE_BUS_MIRROR_CONFIG_DST_BUSID(bus) | DPE_BUS_MIRROR_CONFIG_DST_MB_NUM(subQid);
    writel(value, dpeBase + DPE_BUS_MIRROR_CONFIG_OFFSET);
}

void Dpe_Lld_ConfigSubqueue(uint32_t dpeBase, uint8_t canId, uint8_t subQid,
                            Dpe_SubqueueArbitSchemeType scheme, bool enReplace)
{
    uint32_t value;

    value = readl(dpeBase + DPE_CAN_SUBQUEUE_CONFIG_OFFSET(canId));
    value &= ~(DPE_CAN_SUBQUEUE_CONFIG_SQ_ARBIT_SCHEME(DPE_SUBQUEUE_CONFIG_SQ_ARBIT_SCHEME_MASK, subQid) |
                DPE_CAN_SUBQUEUE_CONFIG_SQ_REPLACE_EN(DPE_SUBQUEUE_CONFIG_SQ_REPLACE_EN_MASK, subQid));

    value |= (DPE_CAN_SUBQUEUE_CONFIG_SQ_ARBIT_SCHEME(scheme, subQid) |
            DPE_CAN_SUBQUEUE_CONFIG_SQ_REPLACE_EN((enReplace ? 1U : 0U) <<
                                                    DPE_SUBQUEUE_CONFIG_SQ_REPLACE_EN_SHIFT, subQid));

    writel(value, dpeBase + DPE_CAN_SUBQUEUE_CONFIG_OFFSET(canId));
}

void Dpe_Lld_ConfigCan(uint32_t dpeBase, uint8_t canId, uint32_t canBufferBaseAddr,
                            uint32_t canFDBaseAddr, uint32_t canReqMask)
{
    Dpe_Lld_ConfigCanBufferBaseAddr(dpeBase, canId, canBufferBaseAddr);
    Dpe_Lld_ConfigCanFDBaseAddr(dpeBase, canId, canFDBaseAddr);
    Dpe_Lld_MaskCanReq(dpeBase, canId, canReqMask);
    /* prevent tx queue from buffering packet before can controller configured. */
    DpeQueue_Lld_QueueFlush(dpeBase, canId, true);
}

void DpePfc_Lld_Config(uint32_t dpeBase, uint8_t pfcIdx, Dpe_PfcCfgType *cfg)
{
    uint32_t offset, value;
    uint32_t frameidStart, frameidEnd;

    offset = DPE_PACKET_FILTER_CONFIG_OFFSET(pfcIdx);
    value = readl(dpeBase + offset);
    value &= ~(DPE_PACKET_FILTER_CONFIG_FMOD_MASK |
                DPE_PACKET_FILTER_CONFIG_FTYPE_MASK |
                DPE_PACKET_FILTER_CONFIG_FSEL_MASK |
                DPE_PACKET_FILTER_CONFIG_FBUSID_MASK);

    value |= (DPE_PACKET_FILTER_CONFIG_FMOD(cfg->matchMod) |
                DPE_PACKET_FILTER_CONFIG_FTYPE(cfg->actType) |
                DPE_PACKET_FILTER_CONFIG_FSEL(cfg->dirSel) |
                DPE_PACKET_FILTER_CONFIG_FBUSID(cfg->busId));
    writel(value, dpeBase + offset);

    if ((cfg->frameProt == DPE_CAN_FRAME_PROT_STD_CC) ||
            (cfg->frameProt == DPE_CAN_FRAME_PROT_STD_FD))
    {
        frameidStart = DPE_FFID_START_FFID_START(cfg->frameidStart << 18U);
        frameidEnd = DPE_FFID_END_FFID_END(cfg->frameidEnd << 18U);
    }
    else
    {
        frameidStart = DPE_FFID_START_FFID_START(cfg->frameidStart);
        frameidEnd = DPE_FFID_END_FFID_END(cfg->frameidEnd);
    }
    writel(frameidStart, dpeBase + DPE_FFID_START_OFFSET(pfcIdx));
    writel(frameidEnd, dpeBase + DPE_FFID_END_OFFSET(pfcIdx));

    offset = DPE_PFC_PROT_OFFSET(pfcIdx);
    value = DPE_PFC_PROT_PROT(cfg->frameProt);
    writel(value, dpeBase + offset);
}

void DpePfc_Lld_Enable(uint32_t dpeBase, uint8_t pfcIdx, bool en)
{
    uint32_t value;

    /* clear flow counter which is out of date. */
    value = readl(dpeBase + DPE_PACKET_FILTER_CONFIG_OFFSET(pfcIdx));
    if (true == en)
    {
        value |= (DPE_PACKET_FILTER_CONFIG_FEN_MASK |
                    DPE_PACKET_FILTER_CONFIG_FLOW_COUNTER_CLR_MASK);
    }
    else
    {
        value &= ~DPE_PACKET_FILTER_CONFIG_FEN_MASK;
    }
    writel(value, dpeBase + DPE_PACKET_FILTER_CONFIG_OFFSET(pfcIdx));
}

uint32_t DpePfc_Lld_GetCounter(uint32_t dpeBase, uint8_t pfcIdx)
{
    return (readl(dpeBase + DPE_PACKET_FILTER_CONFIG_OFFSET(pfcIdx)) &
                DPE_PACKET_FILTER_CONFIG_FLOW_COUNTER_MASK) >>
                DPE_PACKET_FILTER_CONFIG_FLOW_COUNTER_SHIFT;
}

void DpeQueue_Lld_Config(uint32_t dpeBase, uint8_t txqueueIdx, Dpe_CanMbCfgType *cfg)
{
    uint32_t value;

    value = readl(dpeBase + DPE_CAN_TXQUEUE_CONFIG_OFFSET(txqueueIdx));
    value &= ~(DPE_CAN_TXQUEUE_CONFIG_SUB_QUEUE_MB_START_ADDR_MASK |
                DPE_CAN_TXQUEUE_CONFIG_MB_SEL_CTRL_MASK |
                DPE_CAN_TXQUEUE_CONFIG_MB_MODE_MASK |
                DPE_CAN_TXQUEUE_CONFIG_PROTOCOL_CONVERT_MASK |
                DPE_CAN_TXQUEUE_CONFIG_CAN_INTERRUPT_MODE_MASK |
                DPE_CAN_TXQUEUE_CONFIG_TIMEOUT_CNT_MASK);

    /* Default config for txqueue: enable protocol convert and interrupt mode.
     * Timeout cnt value refer to macro @DPE_CAN_TXQUEUE_TIMEOUT_CNT.
     */
    value |= (DPE_CAN_TXQUEUE_CONFIG_SUB_QUEUE_MB_START_ADDR(cfg->mbStartOffset) |
                DPE_CAN_TXQUEUE_CONFIG_MB_SEL_CTRL(cfg->mbSel) |
                DPE_CAN_TXQUEUE_CONFIG_MB_MODE(cfg->mbMod) |
                DPE_CAN_TXQUEUE_CONFIG_TIMEOUT_CNT(DPE_CAN_TXQUEUE_TIMEOUT_CNT) |
                DPE_CAN_TXQUEUE_CONFIG_PROTOCOL_CONVERT_MASK |
                DPE_CAN_TXQUEUE_CONFIG_CAN_INTERRUPT_MODE_MASK);

    writel(value, dpeBase + DPE_CAN_TXQUEUE_CONFIG_OFFSET(txqueueIdx));
}

void DpeQueue_Lld_Start(uint32_t dpeBase, uint8_t txqueueIdx)
{
    DpeQueue_Lld_AllowTimeout(dpeBase, txqueueIdx, false);
    DpeQueue_Lld_QueueFlush(dpeBase, txqueueIdx, false);
}

int DpeQueue_Lld_Stop(uint32_t dpeBase, uint8_t txqueueIdx, bool mbInactived)
{
    int ret = DPE_NOK;
    uint32_t timeoutCnt = DPE_EN_TIMEOUT_COUNTER;

    if (true == mbInactived)
    {
        DpeQueue_Lld_AllowTimeout(dpeBase, txqueueIdx, true);
    }

    DpeQueue_Lld_QueueFlush(dpeBase, txqueueIdx, true);
    while (timeoutCnt--) {
        if (DpeQueue_Lld_QueueFlushDone(dpeBase, txqueueIdx))
        {
            ret = DPE_OK;
            break;
        }
    }

    /*
     * ERRATA DIPDPE-9:
     * Delay 2us to ensure that timeout interrupt is generated if has one.
     * The delay time must be long enough to raise a timeout for DPE which operates at 300MHz,
     * which is equal to ((DPE_TIME_COUNTER_UNIT_VAL + 0x1E) × (DPE_CAN_TXQUEUE_TIMEOUT_CNT + 1) / 300).
     */
    LLD_UDELAY(2U);

    if (DPE_OK == ret)
    {
        /* mbInactived means that message buffer may lose message, it is necessary
         * to clear queue sending status to allow subqueues to restart work
         * without waiting for successful transfer of message buffers.
         */
        if (true == mbInactived)
        {
            DpeQueue_Lld_QueueSendingFlush(dpeBase, txqueueIdx, true);
            DpeQueue_Lld_QueueSendingFlush(dpeBase, txqueueIdx, false);
        }
    }

    return ret;
}

#ifdef CFG_PLATFORM_MCAL
#define DPE_STOP_SEC_CODE
#include "Dpe_MemMap.h"
#endif /* #ifdef CFG_PLATFORM_MCAL */

#ifdef __cplusplus
}
#endif
/* End of file */
